
// 导入缓存键生成器
const { generateGeneralCacheKey } = require('../cache/utils/cacheKeyGenerator');
const { getDbPool } = require('../infra/dbClient');
const { getLogger } = require('../logger');
const dbInstance =  getDbPool();
const loggerInstance = getLogger('utils.utils');

// 登录日志相关依赖
const UAParser = require('ua-parser-js');
const IP2Region = require('ip2region').default;
// 创建一个IP2Region实例
const query = new IP2Region();



/**
 * 将扁平菜单数据转换成树形结构
 * @date 2025/10/24 00:10:06
 * @author lyqjob@yeah.net
 *
 * @param {*} values 
 * @returns {{}} 
 */
const getMenusTree= (values)=>{
    
    // 创建ID到菜单对象的映射
    const menuMap = new Map();
    values.forEach(menu => {
        // menu.children = [];
        menuMap.set(menu.id, menu);
    });

    // 构建树形结构
    const treeMenus = [];
    values.forEach(menu => {
        if (menu.parent_id === null) {
            // 根节点
            treeMenus.push(menu);
        } else {
            // 子节点，找到父节点并添加到children中
            const parent = menuMap.get(menu.parent_id);
            if (parent) {
                // 如果父节点还没有children属性，则创建
                if (!parent.children) {
                    parent.children = [];
                }
                parent.children.push(menu);
            }
        }
    });

    const sortTreemenu = sortTree(treeMenus);
    
    return sortTreemenu;
}


/**
 * 递归树形排序
 * @date 2025/10/24 00:10:47
 * @author lyqjob@yeah.net
 *
 * @param {*} tree 
 */
const sortTree = (tree) => {
    if (!tree || tree.length === 0) return;

    tree.forEach(node => {
        if (node.children && node.children.length > 0) {
            node.children.sort((a, b) => a.sort - b.sort);
            sortTree(node.children);
        }
    });

    // 对根节点也进行排序
    tree.sort((a, b) => a.sort - b.sort);
    return tree;
}

/**
 * 检查用户是否为超级管理员
 * @param {Array} userRoles - 用户角色数组
 * @returns {boolean} 是否为超级管理员
 */
const isFlagTrue = (v) => v === 1 || v === '1' || v === true || v === 'true';
const isSuperAdmin = (userRoles) => {
  return Array.isArray(userRoles) && userRoles.some(role => {
    const code = role && role.code ? String(role.code).toUpperCase() : '';
    return code === 'SUPER_ADMIN' || (isFlagTrue(role?.is_system) && isFlagTrue(role?.is_protected));
  });
};
 
/**
 * 获取用户角色（直接从数据库获取，不使用缓存）
 * @param {number} userId - 用户ID
 * @param {Object} db - 数据库连接对象
 * @param {Object} logger - 日志记录器
 * @returns {Promise<Array>} 用户角色数组
 */
const getUserRoles = async (userId, db, logger) => {
  const roleSql = `
    SELECT r.code, r.is_system, r.is_protected 
    FROM role r
    INNER JOIN user_role ur ON r.id = ur.role_id
    WHERE ur.user_id = ? AND r.status = 0 AND r.is_delete = 0
  `;
  const [roleRows] = await db.execute(roleSql, [userId]);
  return roleRows.map(row => ({
    code: row.code,
    is_system: row.is_system,
    is_protected: row.is_protected
  }));
};

/**
 * 检查用户是否为超级管理员（完整版本，包含角色获取）
 * @param {number} userId - 用户ID
 * @param {Object} db - 数据库连接对象
 * @param {Object} logger - 日志记录器
 * @returns {Promise<boolean>} 是否为超级管理员
 */
const checkIfSuperAdmin = async (userId, db, logger) => {
  const userRoles = await getUserRolesWithCache(userId, db, logger);
  return isSuperAdmin(userRoles);
};

/**
 * 根据用户ID直接判断是否为超级管理员（推荐使用的全局方法）
 * @param {number} userId - 用户ID
 * @param {Object} db - 数据库连接对象（可选，默认使用require('../config/db')）
 * @param {Object} logger - 日志记录器（可选，默认使用console）
 * @returns {Promise<boolean>} 是否为超级管理员
 */
const isSuperAdminById = async (userId) => {
  try {
    // 参数验证
    if (!userId || typeof userId !== 'number' && typeof userId !== 'string') {
      loggerInstance.error('isSuperAdminById: 用户ID参数无效', { userId });
      return false;
    }
    
    // 调用现有的完整检查方法
    return await checkIfSuperAdmin(userId, dbInstance, loggerInstance);
  } catch (error) {
    const { getLogger } = require('../logger');
    const loggerInstance = logger || getLogger('utils.utils');
    loggerInstance.error('isSuperAdminById: 检查超级管理员状态失败', { userId, error: error });
    return false;
  }
};

/**
 * 通用缓存装饰器函数
 * 为任何异步函数添加缓存功能，特别适用于数据库查询优化
 * 
 * @param {Function} fn - 要被缓存的异步函数
 * @param {Object} options - 缓存配置选项
 * @param {string} options.keyPrefix - 缓存键前缀，默认为'cache'
 * @param {number} options.expireTime - 缓存过期时间（秒），默认为3600秒（1小时）
 * @param {Function} options.keyGenerator - 自定义缓存键生成函数，接收原函数的参数，返回字符串
 * @param {Function} options.isCacheable - 决定结果是否应该被缓存的函数，接收函数返回值，返回布尔值
 * @param {Function} options.errorHandler - 错误处理函数，当原函数执行出错时调用
 * @param {Function} options.onCacheHit - 缓存命中时的回调函数
 * @param {Function} options.onCacheMiss - 缓存未命中时的回调函数
 * @param {Function} options.onCacheSet - 缓存设置成功时的回调函数
 * @param {Function} options.cacheReadErrorHandler - 缓存读取错误时的回调函数
 * @param {Function} options.cacheWriteErrorHandler - 缓存写入错误时的回调函数
 * @param {Function} options.callbackContext - 为回调函数提供上下文的函数，接收原函数参数数组
 * @returns {Function} 返回一个包装后的函数，具有与原函数相同的签名，但增加了缓存功能
 * 
 * @example
 * // 基本用法
 * const getUserCached = withCache(getUserFunction, {
 *   keyPrefix: 'user',
 *   expireTime: 3600
 * });
 * 
 * @example
 * // 自定义缓存键和错误处理
 * const getProductCached = withCache(getProductById, {
 *   keyGenerator: (id) => `product:${id}`,
 *   errorHandler: (error, id) => {
 *     console.error(`获取产品${id}失败:`, error);
 *     return null;
 *   },
 *   isCacheable: (product) => product !== null
 * });
 * 
 * @example
 * // 使用回调和上下文
 * const getOrderCached = withCache(getOrderById, {
 *   keyPrefix: 'order',
 *   onCacheHit: (orderId, logger) => logger.debug(`缓存命中: 订单${orderId}`),
 *   onCacheMiss: (orderId, logger) => logger.debug(`缓存未命中: 订单${orderId}`),
 *   callbackContext: (args) => args[1] // 假设第二个参数是logger
 * });
 */
const withCache = (fn, options = {}) => {
  // 引入缓存服务
  const cacheService = require('../cache');
  
  const { 
    keyPrefix = 'cache',
    expireTime = 7200,
    keyGenerator,
    errorHandler,
    isCacheable = () => true
  } = options;
  
  // 返回一个新的函数，保持原始函数的参数和上下文
  return async function(...args) {
    // 尝试获取logger对象（通常是参数之一）
    const logger = args.find(arg => arg && typeof arg === 'object' && arg.info && arg.error) || 
                  { info: console.log, error: console.error };
    
    try {
      // 生成缓存键
      let cacheKey;
      if (keyGenerator) {
        cacheKey = keyGenerator(...args);
      } else {
        // 默认生成缓存键：前缀 + 参数字符串化
        const paramsStr = args.map(arg => {
          // 跳过复杂对象，避免缓存键过长或不稳定
          if (typeof arg === 'object' && arg && !(arg instanceof Date)) {
            return arg.constructor.name;
          }
          return String(arg);
        }).join(':');
        cacheKey = `${keyPrefix}:${paramsStr}`;
      }
      
      // 尝试从缓存获取数据
      let cachedData = await cacheService.get(cacheKey);
      
      if (cachedData !== undefined && cachedData !== null) {
        logger.info('缓存命中', { cacheKey });
        return cachedData;
      }
      
      logger.info('缓存未命中，执行原始函数', { cacheKey });
      
      // 缓存未命中，执行原始函数
      const result = await fn.apply(this, args);
      
      // 判断结果是否可缓存
      if (isCacheable(result)) {
        // 将结果存入缓存
        await cacheService.set(cacheKey, result, expireTime);
        logger.info('缓存已设置', { cacheKey, expireTime });
      }
      
      return result;
    } catch (error) {
      logger.error('缓存操作失败', { error: error, functionName: fn.name });
      
      // 如果提供了错误处理函数，调用它
      if (errorHandler) {
        return errorHandler(error, ...args);
      }
      
      // 默认错误处理：重新抛出错误
      throw error;
    }
  };
};

/**
 * 获取用户角色并缓存结果
 * 使用withCache装饰器实现
 * @param {number} userId - 用户ID
 * @param {Object} db - 数据库连接对象
 * @param {Object} logger - 日志记录器
 * @returns {Promise<Array>} 用户角色数组
 */

const getUserRolesWithCache = withCache(getUserRoles, {
  keyPrefix: 'user_roles',
  expireTime: 7200, // 2小时过期
  // 使用统一的缓存键生成函数
  keyGenerator: (userId) => generateGeneralCacheKey('user', 'roles', userId),
  // 错误处理函数
  errorHandler: (error, userId, db, logger) => {
    logger && logger.error('获取用户角色失败', { userId, error: error });
    // 发生错误时返回空数组，避免影响主流程
    return [];
  },
  // 自定义日志处理
  onCacheHit: (userId, logger) => {
    logger && logger.debug(`从缓存获取用户角色: userId=${userId}`);
  },
  onCacheMiss: (userId, logger) => {
    logger && logger.debug(`缓存未命中，从数据库查询用户角色: userId=${userId}`);
  },
  onCacheSet: (userId, logger) => {
    logger && logger.debug(`设置用户角色缓存: userId=${userId}`);
  },
  // 传递logger参数给回调函数
  callbackContext: (args) => args[2] // args[2]是logger参数
});

/**
 * 示例：使用缓存装饰器包装getUserRoles函数
 * 展示基本用法
 */
const getUserRolesCached = withCache(getUserRoles, {
  keyPrefix: 'user_roles',
  expireTime: 7200, // 2小时过期
  // 自定义缓存键生成函数
  keyGenerator: (userId) => `pc:user:roles:${userId}`,
  // 错误处理函数
  errorHandler: (error, userId) => {
    console.error('获取用户角色缓存失败，返回空数组', { userId, error: error });
    return [];
  }
});

/**
 * 示例：带条件的缓存
 * 只缓存非空结果
 */
const getSomethingWithConditionalCache = withCache(async (id, db, logger) => {
  // 模拟数据库查询
  const result = await db.query('SELECT * FROM something WHERE id = ?', [id]);
  return result;
}, {
  keyPrefix: 'something',
  // 只缓存非空结果
  isCacheable: (result) => result && result.length > 0,
  // 更短的过期时间
  expireTime: 3600 // 1小时
});

/**
 * 示例：使用装饰器包装现有函数的另一种方式
 */
async function getMenuById(id, db, logger) {
  const sql = 'SELECT * FROM menu WHERE id = ? AND is_delete = 0';
  const [rows] = await db.execute(sql, [id]);
  return rows[0] || null;
}

// 创建带缓存的版本
const getMenuByIdCached = withCache(getMenuById, {
  keyPrefix: 'menu',
  keyGenerator: (id) => `menu:${id}`,
  expireTime: 1800 // 30分钟
});

/**
 * 解析User-Agent信息
 * @param {string} userAgent - User-Agent字符串
 * @returns {object} 解析后的浏览器和操作系统信息
 */
const parseUserAgent = (userAgent) => {
    try {
        const parser = new UAParser(userAgent);
        const result = parser.getResult();
        return {
            browser: `${result.browser.name || 'Unknown'} ${result.browser.version || ''}`.trim(),
            operatingSystem: `${result.os.name || 'Unknown'} ${result.os.version || ''}`.trim(),
            device: result.device.type || 'desktop',
            engine: result.engine.name || 'Unknown'
        };
    } catch (error) {
        loggerInstance.error('解析User-Agent失败:', error);
        return {
            browser: 'Unknown',
            operatingSystem: 'Unknown',
            device: 'desktop',
            engine: 'Unknown'
        };
    }
};

/**
 * 根据IP地址获取地理位置信息
 * 注意：这是一个模拟函数，实际项目中应该使用IP地理位置服务
 * @param {string} ip - IP地址
 * @returns {string} 地理位置信息
 */
const getLocationByIP = (ip) => {
    try {
        const address = query.search(ip);
        
        if (address) {
            return `${address?.province||'未知'} ${address?.city||'未知'}`;
        }

        return '未知 未知'
    } catch (error) {
        loggerInstance.error('获取IP地理位置失败:', error);
        return '未知 未知';
    }
};

/**
 * 生成会话ID
 * @returns {string} UUID格式的会话ID
 */
const generateSessionId = () => {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0;
        const v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
};

/**
 * 记录用户登录信息
 * @param {object} loginData - 登录数据
 * @returns {Promise<number>} 登录记录ID
 */
const recordUserLogin = async (loginData) => {
    // 登录日志模块已移除，保留会话ID生成以兼容旧调用方
    try {
        const sessionId = generateSessionId();
        return { logId: 0, sessionId };
    } catch (error) {
        loggerInstance.error('生成会话ID失败:', error);
        throw error;
    }
};

/**
 * 记录用户登出信息
 * @param {string} sessionId - 会话ID
 * @returns {Promise<number>} 影响的记录数
 */
const recordUserLogout = async (sessionId) => {
    // 登录日志模块已移除，兼容旧调用方，返回0
    return 0;
};

/**
 * 获取客户端IP地址
 * @param {object} req - Express请求对象
 * @returns {string} IP地址
 */
const getClientIP = (req) => {
    return req.headers['x-forwarded-for'] ||
           req.headers['x-real-ip'] ||
           req.connection.remoteAddress ||
           req.socket.remoteAddress ||
           (req.connection.socket ? req.connection.socket.remoteAddress : null) ||
           req.ip ||
           '127.0.0.1';
};

/**
 * 清理IP地址格式
 * @param {string} ip - IP地址
 * @returns {string} 清理后的IP地址
 */
const cleanIP = (ip) => {
    if (!ip) return '127.0.0.1';
    
    // 处理IPv6映射的IPv4地址
    if (ip.startsWith('::ffff:')) {
        return ip.substring(7);
    }
    
    // 处理IPv6地址
    if (ip.includes(':')) {
        return '127.0.0.1'; // 简化处理，实际项目中可能需要更复杂的逻辑
    }
    
    return ip;
};



// 计算token过期时间
const calculateExpiry = (expiresIn) => {
    const now = Date.now();
    let expiryMs;

    if (typeof expiresIn === 'number') {
        expiryMs = expiresIn * 1000;
    } else {
        const match = expiresIn.match(/^(\d+)([smhd])$/);
        if (!match) throw new Error('参数格式不正确');

        const value = parseInt(match[1]);
        const unit = match[2];

        switch (unit) {
            case 's': expiryMs = value * 1000; break;
            case 'm': expiryMs = value * 60 * 1000; break;
            case 'h': expiryMs = value * 60 * 60 * 1000; break;
            case 'd': expiryMs = value * 24 * 60 * 60 * 1000; break;
            default: expiryMs = value * 1000;
        }
    }

    return new Date(now + expiryMs);
}

module.exports={
    calculateExpiry,
    getMenusTree,
    sortTree,
    isSuperAdmin,
    checkIfSuperAdmin,
    withCache, // 导出装饰器供其他模块使用
    getUserRolesCached, // 导出示例函数
    getSomethingWithConditionalCache,
    getMenuByIdCached,
    isSuperAdminById,
    getUserRolesWithCache,
    // 登录日志相关函数
    parseUserAgent,
    getLocationByIP,
    generateSessionId,
    recordUserLogin,
    recordUserLogout,
    getClientIP,
    cleanIP
}